﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ComponentModel;

namespace MoneyManagerNET.Presentation.ViewModel
{
    using Model;
using System.Diagnostics;
using System.Windows.Input;
    using MoneyManagerNET.Presentation.Infrastructure;
    using MoneyManagerNET.Presentation.ViewModel.Base;

    public enum TransactionType { Deposit,Wirthdraw,Transfer}

    public class TransactionViewModel : ViewModelBase<ITransaction>
    {
        static TraceSource trace = new TraceSource("MVVM");

        public Array PossibleStatuses {get{return Enum.GetValues(typeof(TransactionStatus));} }

        public override void LoadValues(ITransaction m)
        {
            trace.TraceEvent(TraceEventType.Start, 0, "Load Values");
            Trace.Indent();
            try
            {

                base.LoadValues(m);
                if (m == null) return;

                this.Status = (TransactionStatus)m.Status;
                this.FromAccount = m.FromAccount;
                this.ToAccount = m.ToAccount;
                this.Category = m.Category;
                this.SubCategory = m.SubCategory;
                this.Notes = m.Notes;
                this.Amount = m.Amount;
                this.Date = m.Date.Year < 1800 ? DateTime.Now : m.Date;
                this.Contragent = m.Contragent;

                trace.TraceInformation("status:{0}",Status);
                trace.TraceInformation("FromAccount:{0}", FromAccount);
                trace.TraceInformation("ToAccount:{0}", ToAccount);
                trace.TraceInformation("Category:{0}", Category);
                trace.TraceInformation("SubCategory:{0}", SubCategory);
                trace.TraceInformation("Notes:{0}", Notes);
                trace.TraceInformation("Date:{0}", Date);
                trace.TraceInformation("Amount:{0}", Amount);
                trace.TraceInformation("Contragent:{0}", Contragent);


                //infer transaction type based on ContextAccount
                if (Contragent == null)
                {
                    TransactionType = Presentation.ViewModel.TransactionType.Transfer;
                }
                else
                {
                    if (m.FromAccount == ContextAccount)
                    {
                        TransactionType = Presentation.ViewModel.TransactionType.Wirthdraw;
                    }
                    else if (m.ToAccount == ContextAccount)
                        TransactionType = Presentation.ViewModel.TransactionType.Deposit;
                }

                Trace.Unindent();
                trace.TraceEvent(TraceEventType.Stop, 0, "Load Values");
            }
            catch (Exception exp)
            {
                Trace.Unindent();
                trace.TraceEvent(TraceEventType.Error, 0, "ERROR: Load Values : {0}",exp.Message);
                throw;
            }
        }

        public override ITransaction PutValues(ITransaction m)
        {

            m.Status = (byte)this.Status;
            m.FromAccount = this.FromAccount;
            m.ToAccount = this.ToAccount;
            m.Category = this.Category;
            m.SubCategory = this.SubCategory;
            m.Notes = this.Notes;
            m.Amount = this.Amount;
            m.Date = this.Date;
            m.Contragent = this.Contragent;

            var result = base.PutValues(m);

            trace.TraceInformation("status:{0}", Status);
            trace.TraceInformation("FromAccount:{0}", FromAccount);
            trace.TraceInformation("ToAccount:{0}", ToAccount);
            trace.TraceInformation("Category:{0}", Category);
            trace.TraceInformation("SubCategory:{0}", SubCategory);
            trace.TraceInformation("Notes:{0}", Notes);
            trace.TraceInformation("Date:{0}", Date);
            trace.TraceInformation("Amount:{0}", Amount);
            trace.TraceInformation("Contragent:{0}", Contragent);

            return result;

        }

        #region mirrors


        decimal _Amount;
        public decimal Amount { get { return _Amount; } 
            set { 
                _Amount = value; 
                //validation
                this[() => Amount] =( _Amount <= 0)?"must be greater than 0":string.Empty;

                this.OnChange(() => Amount); 
            } 
        }


        DateTime _Date;
        public DateTime Date { 
            get { return _Date; }
            set
            {
                _Date = value;
                
                this[() => Date] = (!(value.Date > DateTime.Today))?"cannot enter old transactions":string.Empty;

                this.OnChange(() => Date);
                
            }
        }


        string _Notes;
        public string Notes { get { return _Notes; } set { _Notes = value; this.OnChange(() => Notes); } }

        TransactionStatus _Status;
        public TransactionStatus Status { get { return _Status; } set { _Status = value; this.OnChange(() => Status); } }

        private TransactionType _transactionType;
        public TransactionType TransactionType
        {
            get { return _transactionType; }
            set
            {
                _transactionType = value;

                AssociateToType();
            }
        }

        private void AssociateToType()
        {
            switch (TransactionType)
            {
                case TransactionType.Deposit:
                    if (ContextAccount != null)
                        ToAccount = ContextAccount;
                    FromAccount = null;

                    FromVisible = false;
                    ToVisible = false;

                    ContragentTitle = "Payer";
                    ContragentVisible = true;

                    break;

                case TransactionType.Wirthdraw:
                    if (ContextAccount != null)
                        FromAccount = ContextAccount;
                    ToAccount = null;


                    FromVisible = false;
                    ToVisible = false;

                    ContragentTitle = "Reciever";
                    ContragentVisible = true;

                    break;

                case TransactionType.Transfer:

                    FromVisible = true;
                    ToVisible = true;

                    ContragentVisible = false;
                    Contragent = null;
                    break;
            }
        }

        IBankAccount _FromAccount;
        public IBankAccount FromAccount { get { return _FromAccount; } set { _FromAccount = value; this.OnChange(() => FromAccount); this.OnChange(() => FromContent); } }


        IBankAccount _ToAccount;
        public IBankAccount ToAccount { get { return _ToAccount; } set { _ToAccount = value; this.OnChange(() => ToAccount); this.OnChange(() => ToContent); } }


        ICategory _Category;
        public ICategory Category { 
            get { return _Category; } 
            set {
                _Category = value; 
                //validation
                this[()=>CategoryContent]=(Category==null)?"you must select a category":string.Empty;

                this.OnChange(() => Category); this.OnChange(() => CategoryContent); 
            } 
        }


        ISubCategory _SubCategory;
        public ISubCategory SubCategory { get { return _SubCategory; } set { _SubCategory = value; this.OnChange(() => SubCategory); this.OnChange(() => CategoryContent); } }

        IContragent _Contragent;
        public IContragent Contragent { get { return _Contragent; } set { _Contragent = value; this.OnChange(() => Contragent); this.OnChange(() => ContragentContent); } }


#endregion

        #region presentation properties

        public IBankAccount ContextAccount;

        public Array PossibleTypes { get { return Enum.GetValues(typeof(TransactionType)); } }

        bool toVisible;
        public bool ToVisible
        {
            get { return toVisible; }
            set { toVisible = value; this.OnChange(() => ToVisible); }
        }

        bool fromVisible;
        public bool FromVisible
        {
            get { return fromVisible; }
            set { fromVisible = value; this.OnChange(() => FromVisible); }
        }

        bool contragentVisible;
        public bool ContragentVisible
        {
            get { return contragentVisible; }
            set { contragentVisible = value; this.OnChange(() => ContragentVisible); }
        }
        
        
        //string _fromContent="select from account";
        public string FromContent
        {
            get { var str = (FromAccount == null) ? "select from account" : FromAccount.Name; return str; }
        }

        //string _toContent = "select to account";
        public string ToContent
        {
            get {
                var str = (ToAccount == null) ? "select to account" : ToAccount.Name;return str;
            }            
        }

        
        public string CategoryContent
        {
            get
            {
                return  (Category == null) ? "" : (Category.Name + ":")
                  + ((SubCategory == null) ? "" : SubCategory.Name);
            }
        }


        string _contragentTitle;
        public string ContragentTitle
        {
            get { return _contragentTitle; }
            set { _contragentTitle = value; this.OnChange(() => ContragentTitle); }
        }

        public string ContragentContent
        {
            get
            {
                var str = string.Empty;
                if (Contragent == null)
                {
                    switch (TransactionType)
                    {
                        case TransactionType.Deposit:
                            str = "select payer";
                            break;
                        case TransactionType.Wirthdraw:
                            str = "select reciever";
                            break;
                        case TransactionType.Transfer:
                            break;
                        default:
                            break;
                    }
                }
                else str = Contragent.Name;

                return str;
            }
        }

        #endregion

        #region commands

        public void PromptToAccount()
        {
            var vm = new AccountListViewModel();
            var r=vm.ShowDialog();
            if (r.HasValue && r.Value)
            {
                ToAccount = vm.Selected;
                this.OnChange(() => ToContent);
            }
            
        }
        public ICommand PromptToAccountCommand { get; protected set; }
        
        public void PromptFromAccount()
        {
            var vm = new AccountListViewModel();
            var r=vm.ShowDialog();
            if (r.HasValue && r.Value)
            {
                FromAccount = vm.Selected;
                this.OnChange(() => FromContent); 
            }
        }
        public ICommand PromptFromAccountCommand { get; protected set; }

        public void PromptCategory()
        {
            var cvm = new Presentation.ViewModel.CategoryListViewModel();
            var r=cvm.ShowDialog();
            if (r.HasValue && r.Value)
            {
                Category = cvm.Category;
                SubCategory = cvm.SubCategory;
                
            }
        }
        public ICommand PromptCategoryCommand { get; protected set; } 

        public void PromptContragent()
        {
            var cvm = new Presentation.ViewModel.ContragentListViewModel();
            Contragent = cvm.PromptSelect();

        }
        public ICommand PromptContragentCommand { get; protected set; }

        #endregion

        public TransactionViewModel()
        {
            PromptToAccountCommand = new DelegateCommand(o=>this.PromptToAccount());
            PromptFromAccountCommand = new DelegateCommand(o=>this.PromptFromAccount());
            PromptContragentCommand = new DelegateCommand(o => this.PromptContragent());
            PromptCategoryCommand = new DelegateCommand(o=>this.PromptCategory());
        }
    }
}
